<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<meta name="keywords" content="Seata" />
	<meta name="description" content="Seata 微服务框架支持。" />
	<!-- 网页标签标题 -->
	<title>Seata 微服务框架支持</title>
  <link rel="shortcut icon" href="/img/seata_logo_small.jpeg"/>
	<link rel="stylesheet" href="/build/documentation.css" />
</head>
<body>
	<div id="root"><div class="documentation-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/seata_logo.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/system/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a href="/zh-cn/index.html" target="_self">首页</a></li><li class="menu-item menu-item-normal menu-item-normal-active"><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">文档</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/docs/developers/developers_dev.html" target="_self">开发者</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/blog/index.html" target="_self">博客</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/community/index.html" target="_self">社区</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/blog/download.html" target="_self">下载</a></li></ul></div></div></header><div class="bar"><div class="bar-body"><img src="https://img.alicdn.com/tfs/TB1cm8nJwDqK1RjSZSyXXaxEVXa-160-160.png" class="front-img"/><span>文档</span><img src="https://img.alicdn.com/tfs/TB1cm8nJwDqK1RjSZSyXXaxEVXa-160-160.png" class="back-img"/></div></div><section class="content-section"><div class="sidemenu"><div class="sidemenu-toggle"><img src="https://img.alicdn.com/tfs/TB1E6apXHGYBuNjy0FoXXciBFXa-200-200.png"/></div><ul><li class="menu-item menu-item-level-1"><span>概述</span><ul><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">Seata 是什么？</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/overview/terminology.html" target="_self">术语表</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/overview/faq.html" target="_self">FAQ</a></li></ul></li><li class="menu-item menu-item-level-1"><span>用户文档</span><ul><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/quickstart.html" target="_self">快速启动</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/saga.html" target="_self">Saga 模式</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/configurations.html" target="_self">参数配置</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/transaction-group.html" target="_self">事务分组介绍</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/spring.html" target="_self">Spring 支持</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/api.html" target="_self">API 支持</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/microservice.html" target="_self">微服务框架支持</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/ormframework.html" target="_self">ORM 框架支持</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/user/datasource.html" target="_self">数据源类型支持</a></li></ul></li><li class="menu-item menu-item-level-1"><span>开发者指南</span><ul><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><span>各事务模式<img style="transform:rotate(-90deg)" class="menu-toggle" src="/img/system/arrow_down.png"/></span><ul><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/dev/mode/at-mode.html" target="_self">Seata AT 模式</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/dev/mode/tcc-mode.html" target="_self">Seata TCC 模式</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/dev/mode/saga-mode.html" target="_self">Seata Saga 模式</a></li></ul></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/dev/seata-mertics.html" target="_self">Metrics设计</a></li></ul></li><li class="menu-item menu-item-level-1"><span>运维指南</span><ul><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><a href="/zh-cn/docs/ops/operation.html" target="_self">Metrics配置</a></li><li style="height:36px;overflow:hidden" class="menu-item menu-item-level-2"><span>部署<img style="transform:rotate(-90deg)" class="menu-toggle" src="/img/system/arrow_down.png"/></span><ul><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/ops/deploy-guide-beginner.html" target="_self">新人文档</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/ops/deploy-server.html" target="_self">直接部署</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/ops/deploy-by-docker.html" target="_self">Docker部署</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/ops/deploy-by-kubernetes.html" target="_self">Kubernetes部署</a></li><li class="menu-item menu-item-level-3"><a href="/zh-cn/docs/ops/deploy-by-helm.html" target="_self">Helm 部署</a></li></ul></li></ul></li></ul></div><div class="doc-content markdown-body"><h1>事务上下文</h1>
<p>Seata 的事务上下文由 RootContext 来管理。</p>
<p>应用开启一个全局事务后，RootContext 会自动绑定该事务的 XID，事务结束（提交或回滚完成），RootContext 会自动解绑 XID。</p>
<pre><code class="language-java"><span class="hljs-comment">// 绑定 XID</span>
RootContext.bind(xid);

<span class="hljs-comment">// 解绑 XID</span>
String xid = RootContext.unbind();
</code></pre>
<p>应用可以通过 RootContext 的 API 接口来获取当前运行时的全局事务 XID。</p>
<pre><code class="language-java"><span class="hljs-comment">// 获取 XID</span>
String xid = RootContext.getXID();
</code></pre>
<p>应用是否运行在一个全局事务的上下文中，就是通过 RootContext 是否绑定 XID 来判定的。</p>
<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">inGlobalTransaction</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> CONTEXT_HOLDER.get(KEY_XID) != <span class="hljs-keyword">null</span>;
    }
</code></pre>
<h1>事务传播</h1>
<p>Seata 全局事务的传播机制就是指事务上下文的传播，根本上，就是 XID 的应用运行时的传播方式。</p>
<p><em>1. 服务内部的事务传播</em></p>
<p>默认的，RootContext 的实现是基于 <em>ThreadLocal</em> 的，即 XID 绑定在当前线程上下文中。</p>
<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadLocalContextCore</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContextCore</span> </span>{

    <span class="hljs-keyword">private</span> ThreadLocal&lt;Map&lt;String, String&gt;&gt; threadLocal = <span class="hljs-keyword">new</span> ThreadLocal&lt;Map&lt;String, String&gt;&gt;() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">protected</span> Map&lt;String, String&gt; <span class="hljs-title">initialValue</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> HashMap&lt;String, String&gt;();
        }

    };

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">put</span><span class="hljs-params">(String key, String value)</span> </span>{
        <span class="hljs-keyword">return</span> threadLocal.get().put(key, value);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">get</span><span class="hljs-params">(String key)</span> </span>{
        <span class="hljs-keyword">return</span> threadLocal.get().get(key);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">remove</span><span class="hljs-params">(String key)</span> </span>{
        <span class="hljs-keyword">return</span> threadLocal.get().remove(key);
    }
}
</code></pre>
<p>所以服务内部的 XID 传播通常是天然的通过同一个线程的调用链路串连起来的。默认不做任何处理，事务的上下文就是传播下去的。</p>
<p>如果希望挂起事务上下文，则需要通过 RootContext 提供的 API 来实现：</p>
<pre><code class="language-java"><span class="hljs-comment">// 挂起（暂停）</span>
String xid = RootContext.unbind();

<span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> 运行在全局事务外的业务逻辑</span>

<span class="hljs-comment">// 恢复全局事务上下文</span>
RootContext.bind(xid);

</code></pre>
<p><em>2. 跨服务调用的事务传播</em></p>
<p>通过上述基本原理，我们可以很容易理解：</p>
<blockquote>
<p>跨服务调用场景下的事务传播，本质上就是要把 XID 通过服务调用传递到服务提供方，并绑定到 RootContext 中去。</p>
</blockquote>
<p>只要能做到这点，理论上 Seata 可以支持任意的微服务框架。</p>
<h1>对 Dubbo 支持的解读</h1>
<p>下面，我们通过内置的对 Dubbo RPC 支持机制的解读，来说明 Seata 在实现对一个特定微服务框架支持的机制。</p>
<p>对 Dubbo 的支持，我们利用了 Dubbo 框架的 <em>org.apache.dubbo.rpc.Filter</em> 机制。</p>
<pre><code class="language-java"><span class="hljs-comment">/**
 * The type Transaction propagation filter.
 */</span>
<span class="hljs-meta">@Activate</span>(group = { Constants.PROVIDER, Constants.CONSUMER }, order = <span class="hljs-number">100</span>)
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TransactionPropagationFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger LOGGER = LoggerFactory.getLogger(TransactionPropagationFilter.class);

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker&lt;?&gt; invoker, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{
        String xid = RootContext.getXID(); <span class="hljs-comment">// 获取当前事务 XID</span>
        String rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID); <span class="hljs-comment">// 获取 RPC 调用传递过来的 XID</span>
        <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
            LOGGER.debug(<span class="hljs-string">"xid in RootContext["</span> + xid + <span class="hljs-string">"] xid in RpcContext["</span> + rpcXid + <span class="hljs-string">"]"</span>);
        }
        <span class="hljs-keyword">boolean</span> bind = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">if</span> (xid != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// Consumer：把 XID 置入 RPC 的 attachment 中</span>
            RpcContext.getContext().setAttachment(RootContext.KEY_XID, xid);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span> (rpcXid != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// Provider：把 RPC 调用传递来的 XID 绑定到当前运行时</span>
                RootContext.bind(rpcXid);
                bind = <span class="hljs-keyword">true</span>;
                <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(<span class="hljs-string">"bind["</span> + rpcXid + <span class="hljs-string">"] to RootContext"</span>);
                }
            }
        }
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">return</span> invoker.invoke(invocation); <span class="hljs-comment">// 业务方法的调用</span>

        } <span class="hljs-keyword">finally</span> {
            <span class="hljs-keyword">if</span> (bind) { <span class="hljs-comment">// Provider：调用完成后，对 XID 的清理</span>
                String unbindXid = RootContext.unbind();
                <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(<span class="hljs-string">"unbind["</span> + unbindXid + <span class="hljs-string">"] from RootContext"</span>);
                }
                <span class="hljs-keyword">if</span> (!rpcXid.equalsIgnoreCase(unbindXid)) {
                    LOGGER.warn(<span class="hljs-string">"xid in change during RPC from "</span> + rpcXid + <span class="hljs-string">" to "</span> + unbindXid);
                    <span class="hljs-keyword">if</span> (unbindXid != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// 调用过程有新的事务上下文开启，则不能清除</span>
                        RootContext.bind(unbindXid);
                        LOGGER.warn(<span class="hljs-string">"bind ["</span> + unbindXid + <span class="hljs-string">"] back to RootContext"</span>);
                    }
                }
            }
        }
    }
}
</code></pre>
</div></section><footer class="footer-container"><div class="footer-body"><img src="/img/seata_logo_gray.png"/><p class="docsite-power">website powered by docsite</p><div class="cols-container"><div class="col col-12"><h3>愿景</h3><p>Seata 是一款阿里巴巴开源的分布式事务解决方案，致力于在微服务架构下提供高性能和简单易用的分布式事务服务。</p></div><div class="col col-6"><dl><dt>文档</dt><dd><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">Seata 是什么？</a></dd><dd><a href="/zh-cn/docs/user/quickstart.html" target="_self">快速开始</a></dd><dd><a href="https://github.com/seata/seata.github.io/issues/new" target="_self">报告文档问题</a></dd><dd><a href="https://github.com/seata/seata.github.io" target="_self">在Github上编辑此文档</a></dd></dl></div><div class="col col-6"><dl><dt>资源</dt><dd><a href="/zh-cn/blog/index.html" target="_self">博客</a></dd><dd><a href="/zh-cn/community/index.html" target="_self">社区</a></dd></dl></div></div><div class="copyright"><span>Copyright © 2019 Seata</span></div></div></footer></div></div>
	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
	<script>
		window.rootPath = '';
  </script>
	<script src="/build/documentation.js"></script>
	<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?104e73ef0c18b416b27abb23757ed8ee";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
    </script>
</body>
</html>
